package sketch;

import java.util.LinkedList;
import java.util.List;

import sketch.generator.ChainValueGenerator;
import sketch.generator.SymbolicValueGenerator;
import sketch.generator.ValueGenerator;
import sketch.util.Checker;
import sketch.util.Randomness;

//the udita and astgen style
public class ValueChoice<T extends Object> {
	
	public static ValueGenerator<?> randomChoose(int num, ValueGenerator<?> generator) {
		Checker.checkTrue(num > 0, "The selection num should > 0");
		Checker.checkNull(generator, "The pool should not be null!");
		
		ChainValueGenerator<Object> retGenerator = new ChainValueGenerator<Object>();
		Object[] all = generator.all();
		while (num > 0) {
			Object o = all[Randomness.nextRandomInt(all.length)];
			//pool.add(o);
			retGenerator.add(o);
			num --;
		}
		return retGenerator;
	}
	
	public static ValueGenerator<?> removeValues(ValueGenerator<?> all, ValueGenerator<?> toRemove) {
		Object[] allObjs = all.all();
		Object[] toRemoveObjs = toRemove.all();
		ChainValueGenerator<Object> retGenerator = new ChainValueGenerator<Object>();
		for(Object o1 : allObjs) {
			//should we add o1?
			boolean addo1 = true;
			for(Object o2 : toRemoveObjs) {
				//both are null
				if(o1 == null ) {
					if(o2 == null) {
						addo1 = false;
						break;
					}
				}
				if(o1 == null || o2 == null) {
					continue;
				}
				if(o1.equals(o2)) {
					addo1 = false;
					break;
				}
			}
			if(addo1) {
				retGenerator.add(o1);
			}
		}
		
		return retGenerator;
	}
	
	public static ValueGenerator<Object> getObjectPool(Object[] objs, boolean includeNull) {
		Checker.checkNull(objs, "The object array could not be null.");
		ChainValueGenerator<Object> retGenerator = new ChainValueGenerator<Object>(objs.length);
		if(includeNull) {
			retGenerator.add((Object)null);
		}
		for(Object obj : objs) {
			if(!includeNull) {
				Checker.checkNull(obj, "Null is not allowed for the value pool!");
			}
			retGenerator.add(obj);
		}
		return retGenerator;
		
	}
	
	/**
	 * The range is low bound inclusive, while upbound exclusive
	 * */
	public static ValueGenerator<Integer> getIntRange(int low, int up) {
		Checker.checkTrue(up > low, "The upbound should > low bound");
		ChainValueGenerator<Integer> retGenerator = new ChainValueGenerator<Integer>();
		for(int i = low; i <up; i++) {
			retGenerator.add(i);
		}
		return retGenerator;
	}
	
	public static ValueGenerator<Integer> createIntPool(int...vs) {
		Checker.checkTrue(vs != null, "The input argument vs is not null.");
		ChainValueGenerator<Integer> retGenerator = new ChainValueGenerator<Integer>();
		for(int v : vs) {
			retGenerator.add(v);
		}
		return retGenerator;
	}
	
	public static ValueGenerator<Integer> getIntPool(int size, int low, int up) {
		Checker.checkTrue(size > 0, "The given pool size should > 0");
		Checker.checkTrue(up >= low, "The upbound: " + up + "  should >= low bound: " + low);
		ChainValueGenerator<Integer> retGenerator = new ChainValueGenerator<Integer>();
		for(int i = 0 ; i < size; i++) {
			int v = Randomness.nextRandomInt(up - low + 1);
			retGenerator.add(v + low);
		}
		return retGenerator;
	}
	
	public static ValueGenerator<Object[]> getMockPool() {
		List<Object[]> list = new LinkedList<Object[]>();
		//list.add(new Object[]{"str1", "str2", "str3"});
		return getArgumentPool(list);
	}
	
	public static ValueGenerator<Object[]> getArgumentPool(Object[][] args) {
		Checker.checkNull(args, "Object[][] args could not be null.");
		List<Object[]> argList = new LinkedList<Object[]>();
		for(Object[] arg : args) {
			argList.add(arg);
		}
		return getArgumentPool(argList);
	}
	
	public static ValueGenerator<Object[]> getArgumentPool(List<Object[]> args) {
		Checker.checkNull(args, "The argument list should not be null!");
		ChainValueGenerator<Object[]> retGenerator = new ChainValueGenerator<Object[]>();
		for(Object[] arg : args) {
			Checker.checkNull(arg, "The argument list should not be null!");
			retGenerator.add(arg);
			
		}
		return retGenerator;
	}
	
	//used for symbolic execution
	public static ValueGenerator<Integer> intHole() {
		return getGeneratorWithIntHoles(1);
	}
	
	public static ValueGenerator<Integer> getGeneratorWithIntHoles(int size) {
		ValueGenerator<Integer> generator = new SymbolicValueGenerator<Integer>();
		return generator;
	}
}
